/*-------------------------------------------------------------------------
 *
 * port.h
 *	  Header for src/port/ compatibility functions.
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/port.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef PG_PORT_H
#define PG_PORT_H

#include <ctype.h>

/*  
 * Windows 有足够的专用移植内容，我们将大部分内容转移到  
 * 另一个文件中。  
 * 注意：某些 CYGWIN 包含可能会 #define WIN32。  
 */  
#if defined(WIN32) && !defined(__CYGWIN__)
#include "port/win32_port.h"
#endif

/* socket在WIN32上的定义不同 */
#ifndef WIN32
typedef int pgsocket;

#define PGINVALID_SOCKET (-1)
#else
typedef SOCKET pgsocket;

#define PGINVALID_SOCKET INVALID_SOCKET
#endif

/* 如果平台缺少 socklen_t，我们假设这将有效 */  
#ifndef HAVE_SOCKLEN_T
typedef unsigned int socklen_t;
#endif

/* 非阻塞 */  
extern bool pg_set_noblock(pgsocket sock);
extern bool pg_set_block(pgsocket sock);

/* Unix/Win32 的可移植路径处理（在 path.c 中） */  

extern bool has_drive_prefix(const char *filename);
extern char *first_dir_separator(const char *filename);
extern char *last_dir_separator(const char *filename);
extern char *first_path_var_separator(const char *pathlist);
extern void join_path_components(char *ret_path,
								 const char *head, const char *tail);
extern void canonicalize_path(char *path);
extern void canonicalize_path_enc(char *path, int encoding);
extern void make_native_path(char *path);
extern void cleanup_path(char *path);
extern bool path_contains_parent_reference(const char *path);
extern bool path_is_relative_and_below_cwd(const char *path);
extern bool path_is_prefix_of_path(const char *path1, const char *path2);
extern char *make_absolute_path(const char *path);
extern const char *get_progname(const char *argv0);
extern void get_share_path(const char *my_exec_path, char *ret_path);
extern void get_etc_path(const char *my_exec_path, char *ret_path);
extern void get_include_path(const char *my_exec_path, char *ret_path);
extern void get_pkginclude_path(const char *my_exec_path, char *ret_path);
extern void get_includeserver_path(const char *my_exec_path, char *ret_path);
extern void get_lib_path(const char *my_exec_path, char *ret_path);
extern void get_pkglib_path(const char *my_exec_path, char *ret_path);
extern void get_locale_path(const char *my_exec_path, char *ret_path);
extern void get_doc_path(const char *my_exec_path, char *ret_path);
extern void get_html_path(const char *my_exec_path, char *ret_path);
extern void get_man_path(const char *my_exec_path, char *ret_path);
extern bool get_home_path(char *ret_path);
extern void get_parent_directory(char *path);

/* common/pgfnames.c */  
extern char **pgfnames(const char *path);
extern void pgfnames_cleanup(char **filenames);

#define IS_NONWINDOWS_DIR_SEP(ch)	((ch) == '/')
#define is_nonwindows_absolute_path(filename) \
( \
	IS_NONWINDOWS_DIR_SEP((filename)[0]) \
)

#define IS_WINDOWS_DIR_SEP(ch)	((ch) == '/' || (ch) == '\\')
/* 请参见 path_is_relative_and_below_cwd() 了解我们如何处理 'E:abc'。 */  
#define is_windows_absolute_path(filename) \
( \
	IS_WINDOWS_DIR_SEP((filename)[0]) || \
	(isalpha((unsigned char) ((filename)[0])) && (filename)[1] == ':' && \
	 IS_WINDOWS_DIR_SEP((filename)[2])) \
)

/*
 *	is_absolute_path 和 IS_DIR_SEP
 *
 *	通过在这里使用宏，我们避免了需要在 libpq 中包含 path.c。
 */
#ifndef WIN32
#define IS_DIR_SEP(ch) IS_NONWINDOWS_DIR_SEP(ch)
#define is_absolute_path(filename) is_nonwindows_absolute_path(filename)
#else
#define IS_DIR_SEP(ch) IS_WINDOWS_DIR_SEP(ch)
#define is_absolute_path(filename) is_windows_absolute_path(filename)
#endif

/*
 * 此宏提供了一个集中式的错误号列表，这些错误号识别
 * 先前建立的网络连接的严重失效。
 * 该宏旨在用于 switch 语句，形式为
 * "case ALL_CONNECTION_FAILURE_ERRNOS:"。
 *
 * 注意：这将 EPIPE 和 ECONNRESET 分组在一起，我们认为这表示
 * 可能的服务器崩溃，其他错误则表示网络
 * 连接丢失，却没有证明服务器的状态。实际上
 * 报告错误的地方通常会单独列出 EPIPE 和 ECONNRESET，
 * 同时允许将网络故障泛化报告。
 */
#define ALL_CONNECTION_FAILURE_ERRNOS \
	EPIPE: \
	case ECONNRESET: \
	case ECONNABORTED: \
	case EHOSTDOWN: \
	case EHOSTUNREACH: \
	case ENETDOWN: \
	case ENETRESET: \
	case ENETUNREACH: \
	case ETIMEDOUT

/* 可移植的区域设置初始化 (在 exec.c 中) */
extern void set_pglocale_pgservice(const char *argv0, const char *app);

/* 可移植的方式查找和执行二进制文件 (在 exec.c 中) */
extern int	validate_exec(const char *path);
extern int	find_my_exec(const char *argv0, char *retpath);
extern int	find_other_exec(const char *argv0, const char *target,
							const char *versionstr, char *retpath);
extern char *pipe_read_line(char *cmd, char *line, int maxsize);

/* 不属于这里，但与 find_other_exec() 一起使用，因此... */
#define PG_BACKEND_VERSIONSTR "postgres (PostgreSQL) " PG_VERSION "\n"

#ifdef EXEC_BACKEND
/* 在 exec 之前禁用 ASLR，仅用于开发者构建 (在 exec.c 中) */
extern int	pg_disable_aslr(void);
#endif


#if defined(WIN32) || defined(__CYGWIN__)
#define EXE ".exe"
#else
#define EXE ""
#endif

#if defined(WIN32) && !defined(__CYGWIN__)
#define DEVNULL "nul"
#else
#define DEVNULL "/dev/null"
#endif

/* 可移植延迟处理 */
extern void pg_usleep(long microsec);

/* 可移植的 SQL 风格大小写无关比较和转换 */
extern int	pg_strcasecmp(const char *s1, const char *s2);
extern int	pg_strncasecmp(const char *s1, const char *s2, size_t n);
extern unsigned char pg_toupper(unsigned char ch);
extern unsigned char pg_tolower(unsigned char ch);
extern unsigned char pg_ascii_toupper(unsigned char ch);
extern unsigned char pg_ascii_tolower(unsigned char ch);

/*
 * 从 v12 开始，我们始终用我们自己的实现替换 snprintf() 及其相关函数。
 * 该符号不再被核心代码引用，
 * 但仍然保留以防任何扩展查看它。
 */
#define USE_REPL_SNPRINTF 1

/*
 * libintl 版本 >= 0.13 尝试用
 * 宏替换 printf() 和相关函数，以理解 %$ 格式。我们也这样做，
 * 因此禁用它们的宏（如果存在）。
 */
#ifdef vsnprintf
#undef vsnprintf
#endif
#ifdef snprintf
#undef snprintf
#endif
#ifdef vsprintf
#undef vsprintf
#endif
#ifdef sprintf
#undef sprintf
#endif
#ifdef vfprintf
#undef vfprintf
#endif
#ifdef fprintf
#undef fprintf
#endif
#ifdef vprintf
#undef vprintf
#endif
#ifdef printf
#undef printf
#endif

extern int	pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args);
extern int	pg_snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3, 4);
extern int	pg_vsprintf(char *str, const char *fmt, va_list args);
extern int	pg_sprintf(char *str, const char *fmt,...) pg_attribute_printf(2, 3);
extern int	pg_vfprintf(FILE *stream, const char *fmt, va_list args);
extern int	pg_fprintf(FILE *stream, const char *fmt,...) pg_attribute_printf(2, 3);
extern int	pg_vprintf(const char *fmt, va_list args);
extern int	pg_printf(const char *fmt,...) pg_attribute_printf(1, 2);

/*
 * 我们使用 __VA_ARGS__ 对 printf 进行预处理，以防止在 format() 属性声明中替换
 * 对 "printf" 格式原型的引用。
 * 不幸的是，这意味着获取 printf 的函数指针
 * 将无法按照我们的期望工作。 （如果你需要这样做，必须显式命名
 * pg_printf。）对于 printf 的兄弟函数，使用
 * 无参数宏，以便函数指针可以正常工作。
 */
#define vsnprintf		pg_vsnprintf
#define snprintf		pg_snprintf
#define vsprintf		pg_vsprintf
#define sprintf			pg_sprintf
#define vfprintf		pg_vfprintf
#define fprintf			pg_fprintf
#define vprintf			pg_vprintf
#define printf(...)		pg_printf(__VA_ARGS__)

/* 这也由 snprintf.c 提供 */
extern int	pg_strfromd(char *str, size_t count, int precision, double value);

/* 用我们自己的、更加稳健的包装器替换 strerror() */
extern char *pg_strerror(int errnum);
#define strerror pg_strerror

/* 对于 strerror_r() 也是如此；注意我们更喜欢 GNU API */
extern char *pg_strerror_r(int errnum, char *buf, size_t buflen);
#define strerror_r pg_strerror_r
#define PG_STRERROR_R_BUFLEN 256	/* strerror_r 的推荐缓冲区大小 */

/* 包装 strsignal()，或在必要时提供我们自己的版本 */
extern const char *pg_strsignal(int signum);

extern int	pclose_check(FILE *stream);

/* 持有时区信息的全局变量。 */
#if defined(WIN32) || defined(__CYGWIN__)
#define TIMEZONE_GLOBAL _timezone
#define TZNAME_GLOBAL _tzname
#else
#define TIMEZONE_GLOBAL timezone
#define TZNAME_GLOBAL tzname
#endif

#if defined(WIN32) || defined(__CYGWIN__)
/*
 *	在并发访问期间，Win32 没有可靠的重命名/取消链接。
 */
extern int	pgrename(const char *from, const char *to);
extern int	pgunlink(const char *path);

/* 首先包含这个，以便后来的包含不会看到这些定义 */
#ifdef _MSC_VER
#include <io.h>
#endif

#define rename(from, to)		pgrename(from, to)
#define unlink(path)			pgunlink(path)
#endif							/* defined(WIN32) || defined(__CYGWIN__) */

/*
 *	Win32 也没有符号链接，但我们可以在较新的 Win32 版本中
 *	用交叉点模拟它们。
 *
 *	Cygwin 有自己的符号链接，在 Win95/98/ME 上有效，而
 *	交叉点则无效，因此使用这些。我们无法知道
 *	Cygwin 二进制文件将运行在哪种类型的系统上。
 *		注意：某些 CYGWIN 包含可能 #define WIN32。
 */
#if defined(WIN32) && !defined(__CYGWIN__)
extern int	pgsymlink(const char *oldpath, const char *newpath);
extern int	pgreadlink(const char *path, char *buf, size_t size);

#define symlink(oldpath, newpath)	pgsymlink(oldpath, newpath)
#define readlink(path, buf, size)	pgreadlink(path, buf, size)
#endif

extern bool rmtree(const char *path, bool rmtopdir);

#if defined(WIN32) && !defined(__CYGWIN__)

/*
 * 我们想要 lseek() 的 64 位变体。
 *
 * 对于 Visual Studio，这必须在 <io.h> 之后，以避免弄乱其
 * lseek() 和 _lseeki64() 函数声明。
 *
 * 对于 MinGW 已经有宏，因此我们必须取消定义它（取决于
 * _FILE_OFFSET_BITS，它可能指向其自己的 lseek64，但我们不想
 * 指望这一点已设置）。
 */
#undef lseek
#define lseek(a,b,c) _lseeki64((a),(b),(c))

/*
 * 我们想要 chsize() 的 64 位变体。它设置 errno 也返回它，
 * 因此将非零结果转换为 -1 以匹配 POSIX。
 *
 * 防止 MinGW 声明函数，并在我们定义自己的之前取消定义其宏。
 */
#ifndef _MSC_VER
#define FTRUNCATE_DEFINED
#include <unistd.h>
#undef ftruncate
#endif
#define ftruncate(a,b) (_chsize_s((a),(b)) == 0 ? 0 : -1)

/*
 * open() 和 fopen() 的替代品，允许删除打开的文件和
 * 传递其他特殊选项。
 */
#define		O_DIRECT	0x80000000
extern HANDLE pgwin32_open_handle(const char *, int, bool);
extern int	pgwin32_open(const char *, int,...);
extern FILE *pgwin32_fopen(const char *, const char *);
#define		open(a,b,c) pgwin32_open(a,b,c)
#define		fopen(a,b) pgwin32_fopen(a,b)

/*
 * Mingw-w64 头文件将 popen 和 pclose 定义为 _popen 和 _pclose。我们想
 * 使用我们的 popen 包装，而不是普通的 _popen，因此进行覆盖。为了
 * 一致性，也使用我们版本的 pclose。
 */
#ifdef popen
#undef popen
#endif
#ifdef pclose
#undef pclose
#endif

/*
 * system() 和 popen() 的替代，以在额外的
 * 引号对中封闭命令。
 */
extern int	pgwin32_system(const char *command);
extern FILE *pgwin32_popen(const char *command, const char *type);

#define system(a) pgwin32_system(a)
#define popen(a,b) pgwin32_popen(a,b)
#define pclose(a) _pclose(a)

/* 新版本的 MingW 具有 gettimeofday，旧的 mingw 和 msvc 没有 */
#ifndef HAVE_GETTIMEOFDAY
/* 最后一个参数未使用 */
extern int	gettimeofday(struct timeval *tp, struct timezone *tzp);
#endif
#else							/* !WIN32 */

/*
 *	Win32 对于套接字和管道需要特殊关闭，而在 Unix 上
 *	close() 处理所有情况。
 */
#define closesocket close
#endif							/* WIN32 */

/*
 * 在 Windows 上，setvbuf() 不支持 _IOLBF 模式，并将其
 * 解释为 _IOFBF。更糟的是，如果启用了“参数验证”，
 * setvbuf(file, NULL, _IOFBF, 0) 会直接崩溃。因此，在
 * 我们希望选择行缓冲模式的地方，我们会在 Windows 上退回到
 * 无缓冲模式。始终使用 PG_IOLBF 而不是直接使用 _IOLBF
 * 来实现这种行为。
 */
#ifndef WIN32
#define PG_IOLBF	_IOLBF
#else
#define PG_IOLBF	_IONBF
#endif

/*
 * 默认的 "extern" 声明或库例程的宏替代。
 * 当需要时，这些例程由 src/port/ 中的文件提供。
 */

/* 与 fseeko/ftello 一起使用的类型 */
#ifndef WIN32					/* WIN32 在 port/win32_port.h 中处理 */
#define pgoff_t off_t
#endif

#ifndef HAVE_FLS
extern int	fls(int mask);
#endif

#ifndef HAVE_GETPEEREID
/* 在 Windows 上，Perl 可能有不兼容的 uid_t 和 gid_t 定义。 */
#ifndef PLPERL_HAVE_UID_GID
extern int	getpeereid(int sock, uid_t *uid, gid_t *gid);
#endif
#endif

/*
 * Glibc 不使用 clang 的内建功能，因为在一个版本
 * 中存在 *gcc* bug，该版本比 gcc 兼容性 clang 声称具备的
 * 版本更新。这会导致*大量*多余的函数调用，因此在使用
 * clang 时会恢复。在 C++ 中与 libc++（而不是 libstdc++）存在问题，
 * 所以也禁用。
 */
#if defined(__clang__) && !defined(__cplusplus)
/* 需要分开，以免和其他编译器混淆 */
#if __has_builtin(__builtin_isinf)
/* 需要在之前包含，以避免被覆盖 */
#include <math.h>
#undef isinf
#define isinf __builtin_isinf
#endif							/* __has_builtin(isinf) */
#endif							/* __clang__ && !__cplusplus */

#ifndef HAVE_EXPLICIT_BZERO
extern void explicit_bzero(void *buf, size_t len);
#endif

#ifndef HAVE_STRTOF
extern float strtof(const char *nptr, char **endptr);
#endif

#ifdef HAVE_BUGGY_STRTOF
extern float pg_strtof(const char *nptr, char **endptr);
#define strtof(a,b) (pg_strtof((a),(b)))
#endif

#ifndef HAVE_LINK
extern int	link(const char *src, const char *dst);
#endif

#ifndef HAVE_MKDTEMP
extern char *mkdtemp(char *path);
#endif

#ifndef HAVE_INET_ATON
#include <netinet/in.h>
#include <arpa/inet.h>
extern int	inet_aton(const char *cp, struct in_addr *addr);
#endif

/*
 * Windows 和较老的 Unix 没有 pread(2) 和 pwrite(2)。
 * 我们有替代函数，但它们的语义略有不同，因此我们会
 * 使用带有 pg_ 前缀的名称以避免混淆。
 */
#ifdef HAVE_PREAD
#define pg_pread pread
#else
extern ssize_t pg_pread(int fd, void *buf, size_t nbyte, off_t offset);
#endif

#ifdef HAVE_PWRITE
#define pg_pwrite pwrite
#else
extern ssize_t pg_pwrite(int fd, const void *buf, size_t nbyte, off_t offset);
#endif

/* 关于 pg_pwritev() 和 pg_preadv()，请参见 port/pg_iovec.h。 */

#if !HAVE_DECL_STRLCAT
extern size_t strlcat(char *dst, const char *src, size_t siz);
#endif

#if !HAVE_DECL_STRLCPY
extern size_t strlcpy(char *dst, const char *src, size_t siz);
#endif

#if !HAVE_DECL_STRNLEN
extern size_t strnlen(const char *str, size_t maxlen);
#endif

#ifndef HAVE_SETENV
extern int	setenv(const char *name, const char *value, int overwrite);
#endif

#ifndef HAVE_UNSETENV
extern int	unsetenv(const char *name);
#endif

#ifndef HAVE_DLOPEN
extern void *dlopen(const char *file, int mode);
extern void *dlsym(void *handle, const char *symbol);
extern int	dlclose(void *handle);
extern char *dlerror(void);
#endif

/*
 * 在一些较旧的系统中，RTLD_NOW 标志未定义，
 * 并且 dlopen 的模式参数必须始终为 1。
 */
#if !HAVE_DECL_RTLD_NOW
#define RTLD_NOW 1
#endif

/*
 * 如果可用，想要 RTLD_GLOBAL 标志，但并非所有地方都有。
 * 如果不存在，将其设置为 0，这样就没有影响。
 */
#if !HAVE_DECL_RTLD_GLOBAL
#define RTLD_GLOBAL 0
#endif

/* thread.c */
#ifndef WIN32
extern bool pg_get_user_name(uid_t user_id, char *buffer, size_t buflen);
extern bool pg_get_user_home_dir(uid_t user_id, char *buffer, size_t buflen);
#endif

extern void pg_qsort(void *base, size_t nel, size_t elsize,
					 int (*cmp) (const void *, const void *));
extern int	pg_qsort_strcmp(const void *a, const void *b);

#define qsort(a,b,c,d) pg_qsort(a,b,c,d)

typedef int (*qsort_arg_comparator) (const void *a, const void *b, void *arg);

extern void qsort_arg(void *base, size_t nel, size_t elsize,
					  qsort_arg_comparator cmp, void *arg);

extern void qsort_interruptible(void *base, size_t nel, size_t elsize,
								qsort_arg_comparator cmp, void *arg);

extern void *bsearch_arg(const void *key, const void *base,
						 size_t nmemb, size_t size,
						 int (*compar) (const void *, const void *, void *),
						 void *arg);

/* port/chklocale.c */
extern int	pg_get_encoding_from_locale(const char *ctype, bool write_message);

#if defined(WIN32) && !defined(FRONTEND)
extern int	pg_codepage_to_encoding(UINT cp);
#endif

/* port/inet_net_ntop.c */
extern char *pg_inet_net_ntop(int af, const void *src, int bits,
							  char *dst, size_t size);

/* port/pg_strong_random.c */
extern void pg_strong_random_init(void);
extern bool pg_strong_random(void *buf, size_t len);

/*
 * pg_backend_random 以前是针对后台代码的 pg_strong_random 的包装器，
 * 在 Postgres 12 之前。
 */
#define pg_backend_random pg_strong_random

/* port/pgcheckdir.c */
extern int	pg_check_dir(const char *dir);

/* port/pgmkdirp.c */
extern int	pg_mkdir_p(char *path, int omode);

/* port/pqsignal.c（另见 interfaces/libpq/legacy-pqsignal.c） */
#ifdef FRONTEND
#define pqsignal pqsignal_fe
#endif
typedef void (*pqsigfunc) (int signo);
extern pqsigfunc pqsignal(int signo, pqsigfunc func);

/* port/quotes.c */
extern char *escape_single_quotes_ascii(const char *src);

/* common/wait_error.c */
extern char *wait_result_to_str(int exit_status);
extern bool wait_result_is_signal(int exit_status, int signum);
extern bool wait_result_is_any_signal(int exit_status, bool include_command_not_found);

#endif							/* PG_PORT_H */
